﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Data;

namespace FusionDotNet.Tests
{
    [TestClass]
    public class AsyncTests
    {
        private const string USERNAME = "test@gmail.com";
        private const string PASSWORD = "***";
        private const int TEST_NAMES_TABLE = 377942;
        private const int TEST_TEXT_TABLE = 380760;
        private const int PAUSE_DURATION = 1000;
        private static AuthenticationResult m_AuthResult;

        private object m_Result;
        private bool m_Working;
        private SelectResult m_AsyncSelectResult;

        private AuthenticationResult CreateAuthToken()
        {
            if (m_AuthResult == null)
            {
                AuthenticationCommand authCommand = new AuthenticationCommand(AccountTypeEnum.Google, USERNAME, PASSWORD, "fusiontables", "visionforms");
                m_AuthResult = authCommand.Execute();
            }
            return m_AuthResult;
        }

        [TestCategory("SELECT"), TestMethod]
        public void AsyncBasicAuthenticatedSelectWithAsyncResult()
        {
            AuthenticationResult authResult = CreateAuthToken();

            Assert.IsTrue(authResult.IsAuthenticated, "Authentication Failed");

            string commandText = "SELECT ROWID, FirstName, LastName FROM " + TEST_NAMES_TABLE;
            SelectCommand command = new SelectCommand(commandText);
            command.AuthenticationToken = authResult.AuthenticationToken;

            m_Working = true;
            m_AsyncSelectResult = null;
            var result = command.BeginExecute(this.ExecuteComplete, command);

            while (m_Working)
            {
                System.Threading.Thread.Sleep(1000);
            }

            Assert.IsNotNull(m_AsyncSelectResult);
            Assert.IsTrue(m_AsyncSelectResult.ResultTable.Rows.Count > 0);
        }

        [TestCategory("SELECT"), TestMethod]
        public void AsyncBasicAuthenticatedSelectWithAsyncResultWaitOne()
        {
            AuthenticationResult authResult = CreateAuthToken();

            Assert.IsTrue(authResult.IsAuthenticated, "Authentication Failed");

            string commandText = "SELECT ROWID, FirstName, LastName FROM " + TEST_NAMES_TABLE;
            SelectCommand command = new SelectCommand(commandText);
            command.AuthenticationToken = authResult.AuthenticationToken;

            m_Working = true;
            m_AsyncSelectResult = null;
            var result = command.BeginExecute(this.ExecuteComplete, command);

            result.AsyncWaitHandle.WaitOne();

            Assert.IsNotNull(m_AsyncSelectResult);
            Assert.IsTrue(m_AsyncSelectResult.ResultTable.Rows.Count > 0);
        }

        [TestCategory("SELECT"), TestMethod]
        public void AsyncBasicAuthenticatedSelectWithEndInvoke()
        {
            AuthenticationResult authResult = CreateAuthToken();

            Assert.IsTrue(authResult.IsAuthenticated, "Authentication Failed");

            string commandText = "SELECT ROWID, FirstName, LastName FROM " + TEST_NAMES_TABLE;
            SelectCommand command = new SelectCommand(commandText);
            command.AuthenticationToken = authResult.AuthenticationToken;

            m_Working = true;
            m_AsyncSelectResult = null;
            var result = command.BeginExecute(this.ExecuteComplete, command);

            m_AsyncSelectResult = command.EndExecute(result);

            Assert.IsNotNull(m_AsyncSelectResult);
            Assert.IsTrue(m_AsyncSelectResult.ResultTable.Rows.Count > 0);
        }

        private void ExecuteComplete(IAsyncResult result)
        {
            SelectCommand command = result.AsyncState as SelectCommand;
            if (command != null)
            {
                m_AsyncSelectResult = command.EndExecute(result);
                m_Working = false;
            }

        }

        

        [TestCategory("SELECT"), TestMethod]
        public void AsyncBasicAuthenticatedSelect()
        {
            AuthenticationResult authResult = CreateAuthToken();

            Assert.IsTrue(authResult.IsAuthenticated, "Authentication Failed");

            string commandText = "SELECT ROWID, FirstName, LastName FROM " + TEST_NAMES_TABLE;
            SelectCommand command = new SelectCommand(commandText);
            command.AuthenticationToken = authResult.AuthenticationToken;

            command.ExecuteCompletedEvent += new Command<SelectResult>.ExecuteCompletedEventHandler(command_ExecuteCompletedEvent);

            command.ExecuteAsync();
            m_Result = null;
            int waiting = 0;
            while (command.IsBusy)
            {
                System.Threading.Thread.Sleep(1000);
                waiting++;
            }

            System.Diagnostics.Debug.WriteLine(String.Format("Select completed in {0}.", waiting));

            Assert.IsNotNull(m_Result);
            Assert.IsInstanceOfType(m_Result, typeof(SelectResult));
            Assert.IsTrue(((SelectResult)m_Result).ResultTable.Rows.Count > 0);
        }

        [TestCategory("SELECT"), TestMethod]
        public void AsyncBasicAuthenticatedSelectMissingColumn()
        {
            AuthenticationResult authResult = CreateAuthToken();

            Assert.IsTrue(authResult.IsAuthenticated, "Authentication Failed");

            string commandText = "SELECT BadColumnName FROM " + TEST_NAMES_TABLE;
            SelectCommand command = new SelectCommand(commandText);
            command.AuthenticationToken = authResult.AuthenticationToken;

            command.ExecuteCompletedEvent += new Command<SelectResult>.ExecuteCompletedEventHandler(command_ExecuteCompletedEvent);

            command.ExecuteAsync();
            m_Result = null;
            int waiting = 0;
            while (command.IsBusy)
            {
                System.Threading.Thread.Sleep(1000);
                waiting++;
            }

            Assert.IsNotNull(m_Result);
            Assert.IsInstanceOfType(m_Result, typeof(SelectResult));
            Assert.IsTrue(((SelectResult)m_Result).HasExceptions);
            Assert.IsTrue(((SelectResult)m_Result).ResultExceptions.OfType<InvalidOperationException>().Count() > 0);
        }

        [TestCategory("INSERT"), TestMethod]
        public void InsertRowsAsync()
        {
            AuthenticationResult authResult = CreateAuthToken();

            Assert.IsTrue(authResult.IsAuthenticated, "Authentication Failed");

            DataTable updateTable = new DataTable(TEST_NAMES_TABLE.ToString());
            updateTable.Columns.Add("FirstName");
            updateTable.Columns.Add("LastName");

            int rowCounter = 0;
            for (rowCounter = 0; rowCounter < 500; rowCounter++)
            {
                updateTable.Rows.Add(new object[] { "Test Update Row " + rowCounter, DateTime.Now.ToString() });
            }

            InsertCommand command = new InsertCommand(updateTable);
            command.AuthenticationToken = authResult.AuthenticationToken;

            command.ExecuteCompletedEvent += new Command<SelectResult>.ExecuteCompletedEventHandler(command_ExecuteCompletedEvent);

            m_Result = null;
            
            command.ExecuteAsync();

            System.Diagnostics.Debug.WriteLine(string.Format("POST Complete {0}", m_Result != null));

            int waiting = 0;
            while (command.IsBusy)
            {
                if (waiting > 10)
                {
                    throw new TimeoutException();
                }

                System.Threading.Thread.Sleep(1000);

                waiting++;
            }

            Assert.IsNotNull(m_Result);
            Assert.IsInstanceOfType(m_Result, typeof(SelectResult));
            SelectResult result = (SelectResult)m_Result;

            Assert.IsTrue(result.HasExceptions == false);
            Assert.IsTrue(result.ResultTable.Rows.Count == updateTable.Rows.Count);
            Assert.IsTrue(result.ResultTable.Rows[0]["rowid"].ToString() != "");
            Assert.IsTrue(result.ResultTable.Rows[1]["rowid"].ToString() != "");
        }

        void command_ExecuteCompletedEvent(object sender, Command<SelectResult>.ExecuteCompletedEventArgs e)
        {
            lock (this)
            {
                m_Working = false;
            }

            m_Result = e.Result;
        }
         
    }
}
